#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
ls(nomFich), ts() { }

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::analyse() {
    arbre = programme();
    cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::programme() {
    // <programme> ::= debut <seq_inst> fin FIN_FICHIER

    sauterSymCour("debut");
    Noeud* si = seqInst();
    sauterSymCour("fin");
    testerSymCour("<FINDEFICHIER>");
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::seqInst() {
    // <seqInst> ::= <inst> ; { <inst> ; }

    NoeudSeqInst* si = new NoeudSeqInst();
    do {
        si->ajouteInstruction(inst());
        sauterSymCour(";");
    } while (ls.getSymCour() == "<VARIABLE>"
            || ls.getSymCour() == "si"
            || ls.getSymCour() == "tantque"
            || ls.getSymCour() == "repeter"
            || ls.getSymCour() == "lire"
            || ls.getSymCour() == "ecrire");
    // tant que le symbole courant est un debut possible d'instruction...
    return si;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::inst() {
    // <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instLire> | <instEcrire>
    if (ls.getSymCour() == "si") {
        return instSi();
    } else if (ls.getSymCour() == "tantque") {
        return instTq();
    } else if (ls.getSymCour() == "repeter") {
        return instRepeter();
    } else if (ls.getSymCour() == "lire") {
        return instLire();
    } else if (ls.getSymCour() == "ecrire") {
        return instEcrire();
    } else if (ls.getSymCour() == "<VARIABLE>") {
        return affectation();
    } else {
        erreur("instruction");
        return NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
    // <affectation> ::= <variable> = <expression>

    testerSymCour("<VARIABLE>");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour("=");
    Noeud* exp = expBool();
    return new NoeudAffectation(var, exp);
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
    // <expression> ::= <terme> { <opAdd> <terme> }
    Noeud* term = terme();
    while (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
        Noeud* termeDroit = terme(); // lecture de l'operande droit
        term = new NoeudOperateurBinaire(operateur, term, termeDroit); // const. du noeud
    }
    return term;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme() {
    // <terme> ::= <facteur> { <opMult> <facteur> }

    Noeud* fact = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        Symbole operateur = opMult(); // on stocke le symbole de l'opérateur
        Noeud* factDroit = facteur(); // lecture de l'operande droit
        fact = new NoeudOperateurBinaire(operateur, fact, factDroit); // const. du noeud
    }
    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::facteur() {
    // <facteur> ::= <ENTIER> | <REEL> | <CARACTERE> | <CHAINE> |
    //               <variable> | <opUnaire> <expBool> | ( <expBool> )

    Noeud* fact = NULL;

    if (ls.getSymCour() == "<VARIABLE>" || ls.getSymCour() == "<ENTIER>"
            || ls.getSymCour() == "<REEL>" || ls.getSymCour() == "<CARACTERE>"
            || ls.getSymCour() == "<CHAINE>") {
        fact = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    } else if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        Symbole operateur = opUnaire();
        Noeud* exp = expBool();
        // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
        fact = new NoeudOperateurBinaire(operateur, NULL, exp);
    } else if (ls.getSymCour() == "(") {
        ls.suivant();
        fact = expBool();
        sauterSymCour(")");
    } else
        erreur("<facteur>");
    return fact;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd>  ::= + | -
{
    Symbole operateur;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opAdd>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult()
// <opAdd>  ::= + | -
{
    Symbole operateur;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opMult>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expBool()
// <expBool> ::= <termeBool> { ou <termeBool> }
{
    Noeud* terme = termeBool();
    while (ls.getSymCour() == "ou") {
        ls.suivant();
        Noeud* termeDroit = termeBool();
        terme = new NoeudOperateurBinaire(Symbole("ou"), terme, termeDroit);
    }
    return terme;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::termeBool()
// <termeBool> ::= <relation> { et <relation> }
{
    Noeud* rel = relation();
    while (ls.getSymCour() == "et") {
        ls.suivant();
        Noeud* relationDroite = relation();
        rel = new NoeudOperateurBinaire(Symbole("et"), rel, relationDroite);
    }
    return rel;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation()
//  <relation> ::= <expression> { <opRel> <expression> }
{
    Noeud* exp = expression();
    while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" || ls.getSymCour() == ">" || ls.getSymCour() == "<=" || ls.getSymCour() == ">=") {
        Symbole operateur = opRel();
        Noeud* expressionDroite = expression();
        exp = new NoeudOperateurBinaire(operateur, exp, expressionDroite);
    }
    return exp;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel()
//  <opRel> ::= == | != | < | <= | > | >=
{
    Symbole operateur;
    if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == "<" || ls.getSymCour() == ">" || ls.getSymCour() == "<=" || ls.getSymCour() == ">=") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else {
        erreur("<opRel>");
    }
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire()
//  <opUnaire> ::= - | non
{
    Symbole operateur;
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        operateur = ls.getSymCour();
        ls.suivant();
    } else
        erreur("<opUnaire>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instSi()
//  <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi
{
    // Création du si avec le premier cas
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* condSi = expBool();
    sauterSymCour(")");
    Noeud* seqInstSi = seqInst();
    Noeud* instSi = new NoeudInstSi(condSi, seqInstSi);

    // Ajout des cas suivants si existants
    while (ls.getSymCour() == "sinonsi") {
        sauterSymCour("sinonsi");
        sauterSymCour("(");
        Noeud* cond = expBool();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        ((NoeudInstSi*) instSi)->ajouteSinonSi(cond, seq);
    }
    if (ls.getSymCour() == "sinon") {
        sauterSymCour("sinon");
        ((NoeudInstSi*) instSi)->ajouteSinon(seqInst());
    }
    sauterSymCour("finsi");
    return instSi;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instTq()
//  <instTq> ::= tantque ( <expBool> ) <seqInst> fintq
{
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("fintq");
    Noeud* instTq = new NoeudInstTq(cond, seq);
    return instTq;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instRepeter()
//  <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
{
    sauterSymCour("repeter");
    Noeud* seq = seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    Noeud * instRepeter = new NoeudInstRepeter(cond, seq);
    return instRepeter;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instPour()
//  <instPour> ::= pour ( <affectation> ; <expBool> ; <affectation> ) <seqInst> finpour
{
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* aff1 = affectation();
    sauterSymCour(";");
    Noeud* cond = expBool();
    sauterSymCour(";");
    Noeud* aff2 = affectation();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("finpour");
    Noeud* instPour = new NoeudInstPour(aff1, cond, aff2, seq);
    return instPour;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instLire()
//  <instLire> ::= lire ( <variable> )
{
    sauterSymCour("lire");
    sauterSymCour("(");
    SymboleValue* var = ts.chercheAjoute(ls.getSymCour());
    ls.suivant();
    sauterSymCour(")");
    Noeud* instLire = new NoeudInstLire(var);
    return instLire;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::instEcrire()
//  <instEcrire> ::= ecrire ( <expression> )
{
    sauterSymCour("ecrire");
    sauterSymCour("(");
    Noeud* argExp = expression();
    sauterSymCour(")");
    Noeud* instEcrire = new NoeudInstEcrire(argExp);
    return instEcrire;
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    if (ls.getSymCour() != ch) {
        erreur(ch);
    }
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    testerSymCour(ch);
    ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::erreur(string mess) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
            << ls.getColonne() << endl << "   Attendu : " << mess << endl
            << "   Trouve  : " << ls.getSymCour() << endl << endl;
    exit(0); // plus tard, on levera une exception
}
